package com.zys.http.processor;

import cn.hutool.core.text.CharSequenceUtil;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.zys.http.constant.HttpEnum;
import com.zys.http.entity.param.ParamProperty;
import com.zys.http.entity.velocity.MethodItem;
import com.zys.http.processor.jaxrs.JaxRsJavaApiProcessor;
import com.zys.http.processor.jaxrs.JaxRsKotlinApiProcessor;
import com.zys.http.processor.spring.SpringEnums;
import com.zys.http.processor.spring.SpringJavaApiProcessor;
import com.zys.http.processor.spring.SpringKotlinApiProcessor;
import com.zys.http.tool.ThreadTool;
import com.zys.http.ui.tree.node.MethodNode;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
import org.jetbrains.kotlin.psi.KtNamedFunction;

import java.util.*;

/**
 * @author zhou ys
 * @since 2024-05-21
 */
public class ApiProcessorContext {
    private static final Map<String, List<ApiProcessor>> PROCESSOR_MAP = new HashMap<>();

    static {
        // 索引 0-Spring 1-JaxRs
        List<ApiProcessor> javaProcessors = List.of(SpringJavaApiProcessor.INSTANCE, JaxRsJavaApiProcessor.INSTANCE);
        PROCESSOR_MAP.put(JavaLanguage.INSTANCE.getDisplayName(), javaProcessors);

        List<ApiProcessor> kotlinProcessors = List.of(SpringKotlinApiProcessor.INSTANCE, JaxRsKotlinApiProcessor.INSTANCE);
        PROCESSOR_MAP.put(KotlinLanguage.INSTANCE.getDisplayName(), kotlinProcessors);
    }

    @SneakyThrows
    public static List<? extends PsiElement> controllers(Project project, Module module, @NotNull List<HttpEnum.Language> languages) {
        return ReadAction.nonBlocking(() -> {
            if (languages.isEmpty() || languages.size() >= HttpEnum.Language.values().length) {
                return PROCESSOR_MAP.values().stream().flatMap(Collection::stream).map(o -> o.controllers(project, module)).flatMap(Collection::stream).toList();
            } else if (languages.contains(HttpEnum.Language.JAVA)) {
                return PROCESSOR_MAP.get(JavaLanguage.INSTANCE.getDisplayName()).stream().map(o -> o.controllers(project, module)).flatMap(Collection::stream).toList();
            }
            return PROCESSOR_MAP.get(KotlinLanguage.INSTANCE.getDisplayName()).stream().map(o -> o.controllers(project, module)).flatMap(Collection::stream).toList();
        }).submit(ThreadTool.getExecutor()).get();
    }

    public static String packageName(PsiElement psiElement) {
        return PROCESSOR_MAP.get(psiElement.getLanguage().getDisplayName()).get(0).packageName(psiElement);
    }

    public static String controllerSwagger(PsiElement psiElement) {
        return PROCESSOR_MAP.get(psiElement.getLanguage().getDisplayName()).get(0).controllerSwagger(psiElement);
    }

    @SneakyThrows
    public static List<MethodNode> methodNodes(PsiElement psiElement, String contextPath) {
        if (Objects.isNull(psiElement)) {
            return Collections.emptyList();
        }
        return ReadAction.nonBlocking(() -> PROCESSOR_MAP.values().stream()
                .flatMap(Collection::stream)
                .map(o -> o.methodNodes(psiElement, contextPath))
                .flatMap(Collection::stream)
                .toList()
        ).submit(ThreadTool.getExecutor()).get();
    }

    public static Map<String, ParamProperty> methodParametersParse(@NotNull PsiElement element, boolean isJsonPretty) {
        boolean isSpring = false;
        if (element instanceof PsiMethod method) {
            // 判断方法注解来源
            for (PsiAnnotation annotation : method.getAnnotations()) {
                if (SpringEnums.Method.contains(annotation)) {
                    isSpring = true;
                    break;
                }
            }
            return PROCESSOR_MAP.get(element.getLanguage().getDisplayName()).get(isSpring ? 0 : 1).methodParametersParse(element, isJsonPretty);
        } else if (element instanceof KtNamedFunction function) {
            for (KtAnnotationEntry entry : function.getAnnotationEntries()) {
                Name shortName = entry.getShortName();
                if (Objects.isNull(shortName)) {
                    continue;
                }
                String name = shortName.asString();
                if (SpringEnums.Method.contains(name) || SpringEnums.Method.contains("org.springframework.web.bind.annotation." + name)) {
                    isSpring = true;
                    break;
                }
            }
        }
        return PROCESSOR_MAP.get(element.getLanguage().getDisplayName()).get(isSpring ? 0 : 1).methodParametersParse(element, isJsonPretty);
    }

    public static List<MethodItem> postmanMethods(PsiElement psiElement, String contextPath) {
        return PROCESSOR_MAP.values().stream()
                .flatMap(Collection::stream)
                .map(o -> o.postmanMethods(psiElement, contextPath))
                .flatMap(Collection::stream)
                .toList();
    }

    public static String controllerPath(@NotNull PsiElement element) {
        List<String> list = PROCESSOR_MAP.get(element.getLanguage().getDisplayName()).stream().map(o -> o.controllerPath(element)).filter(CharSequenceUtil::isNotEmpty).toList();
        if (list.isEmpty()) {
            return "";
        }
        return list.get(0);
    }

    public static String methodPath(@NotNull PsiElement element) {
        List<String> list = PROCESSOR_MAP.get(element.getLanguage().getDisplayName()).stream().map(o -> o.methodPath(element)).filter(CharSequenceUtil::isNotEmpty).toList();
        if (list.isEmpty()) {
            return "";
        }
        return list.get(0);
    }
}
